﻿using System;
using System.Linq;
using System.Windows.Forms;
using CHomeWeather.Properties;
using System.Text.RegularExpressions;
using System.Collections.ObjectModel;
using Microsoft.Win32;
using System.Collections.Generic;
using System.Xml;
using System.Text;

namespace CHomeWeather
{
    public partial class MainForm : Form
    {
        private const char CHOME_ITEM_SEPARATOR = ';';
        private const string CHOME_EXTENSIBILITY_XML_KEY = "ExtensibilityXML";
        private const string CHOME_ROOTKEY = @"SOFTWARE\Microsoft\CHome";
        private const string CHOME_EXTENSIBILITY_XML_ROOT_NODE = "Plugins";
        private const string CHOME_EXTENSIBILITY_XML_ORDER_ATTRIBUTE = "Order";

        private const string CHOME_WEATHER_NAME = "CHomeWeather";

        private Configuration _configuration;

        public MainForm()
        {
            InitializeComponent();

            LoadConfigurations();
        }

        private void LoadConfigurations()
        {
            _configuration = Configuration.Load(Configuration.CONFIGURATION_FILE_NAME);

            if (null != _configuration.Cities)
            {
                foreach (var city in _configuration.Cities)
                {
                    lstCities.Items.Add(city);
                }
            }

            if (null != _configuration.Schedules)
            {
                foreach (var schedule in _configuration.Schedules)
                {
                    lstSchedules.Items.Add(schedule);
                }
            }
        }

        private void OnAddCityClick(object sender, EventArgs e)
        {
            string city = cbCity.Text.Trim();

            if (string.IsNullOrEmpty(city))
            {
                MessageBox.Show(Resources.WARNING_CITY_IS_EMPTY);
                return;
            }

            if (lstCities.Items.Contains(city))
            {
                MessageBox.Show(Resources.WARNING_CITY_EXISTS);
                return;
            }

            lstCities.Items.Add(city);
        }

        private void OnAddScheduleClick(object sender, EventArgs e)
        {
            string schedule = cbSchedule.Text.Trim();

            if (string.IsNullOrEmpty(schedule))
            {
                MessageBox.Show(Resources.WARNING_SCHEDULE_IS_EMPTY);
                return;
            }

            Match match = Regex.Match(schedule, "^(20|21|22|23|[0-1]?\\d):([0-5]?\\d)$");

            if (!match.Success)
            {
                MessageBox.Show(Resources.WARNING_SCHEDULE_WRONG_FORMAT);
                return;
            }

            DateTime updateTime = DateTime.Now.Date.AddHours(int.Parse(match.Groups[1].Value)).AddMinutes(int.Parse(match.Groups[2].Value));

            var item = lstSchedules.Items.Cast<Schedule>().SingleOrDefault(x => x.UpdateTime.TimeOfDay == updateTime.TimeOfDay);

            if (null != item)
            {
                MessageBox.Show(Resources.WARNING_SCHEDULE_EXISTS);
                return;
            }

            lstSchedules.Items.Add(new Schedule { UpdateTime = updateTime, Occurrence = (cbOccurence.SelectedIndex == 0 ? Occurrence.Daily : Occurrence.Once) });
        }

        private void OnSaveClick(object sender, EventArgs e)
        {
            if (null == _configuration.Cities)
            {
                _configuration.Cities = new Collection<string>();
            }
            else
            {
                _configuration.Cities.Clear();
            }

            foreach (string city in lstCities.Items)
            {
                _configuration.Cities.Add(city);
            }

            if (null == _configuration.Schedules)
            {
                _configuration.Schedules = new Collection<Schedule>();
            }
            else
            {
                _configuration.Schedules.Clear();
            }

            foreach (Schedule schedule in lstSchedules.Items)
            {
                _configuration.Schedules.Add(schedule);
            }

            Configuration.Save(Configuration.CONFIGURATION_FILE_NAME, _configuration);

            Util.SetNextUpdateTime(_configuration);
        }

        private void OnExitClick(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void OnCityListSelectionChanged(object sender, EventArgs e)
        {
            btnDeleteCity.Enabled = lstCities.SelectedIndex >= 0;
        }

        private void OnScheduleListSelectionChanged(object sender, EventArgs e)
        {
            btnDeleteSchedule.Enabled = lstSchedules.SelectedIndex >= 0;
        }

        private void OnDeleteCityClick(object sender, EventArgs e)
        {
            lstCities.Items.RemoveAt(lstCities.SelectedIndex);
        }

        private void OnDeleteScheduleClick(object sender, EventArgs e)
        {
            lstSchedules.Items.RemoveAt(lstSchedules.SelectedIndex);
        }

        private void OnUpdateClick(object sender, EventArgs e)
        {
            Util.Update(false);
        }

        private void OnEnableWeatherClick(object sender, EventArgs e)
        {
            ToggleWeather(true);
        }

        private void OnDisableWeatherClick(object sender, EventArgs e)
        {
            ToggleWeather(false);
        }

        private static void ToggleWeather(bool enabled)
        {
            RegistryKey softwareKey = Registry.LocalMachine.OpenSubKey(CHOME_ROOTKEY, true);
            string extensibilityXML = Convert.ToString(softwareKey.GetValue(CHOME_EXTENSIBILITY_XML_KEY, string.Empty));

            List<string> pluginOrders = LoadOrderedItems(extensibilityXML);

            if (enabled && !pluginOrders.Contains(CHOME_WEATHER_NAME))
            {
                pluginOrders.Add(CHOME_WEATHER_NAME);
            }
            else if (!enabled && pluginOrders.Contains(CHOME_WEATHER_NAME))
            {
                pluginOrders.Remove(CHOME_WEATHER_NAME);
            }

            StringBuilder sb = new StringBuilder();

            var orderedItems = from item in pluginOrders
                               select sb.AppendFormat("{0}{1}", item, CHOME_ITEM_SEPARATOR);

            orderedItems.ToArray();

            string savedXML = SaveOrderedItems(extensibilityXML, sb.ToString());

            softwareKey.SetValue(CHOME_EXTENSIBILITY_XML_KEY, savedXML);

            Util.RefreshToday();
        }

        private static List<string> LoadOrderedItems(string extensibilityXML)
        {
            List<string> orderedList = new List<string>();
            string orderedValue = string.Empty;
            XmlDocument doc = new XmlDocument();

            try
            {
                doc.LoadXml(extensibilityXML);

                XmlNode pluginNode = doc.SelectSingleNode(CHOME_EXTENSIBILITY_XML_ROOT_NODE);

                if (null != pluginNode)
                {
                    orderedValue = pluginNode.Attributes[CHOME_EXTENSIBILITY_XML_ORDER_ATTRIBUTE].Value;
                }
            }
            catch (Exception ex)
            {
                // TODO: add error handling.
            }

            orderedList.AddRange(orderedValue.Split(';'));

            return orderedList;
        }

        private static string SaveOrderedItems(string extensibilityXML, string concatedValue)
        {
            string result = extensibilityXML;
            XmlDocument doc = new XmlDocument();

            try
            {
                doc.LoadXml(extensibilityXML);

                XmlNode pluginNode = doc.SelectSingleNode(CHOME_EXTENSIBILITY_XML_ROOT_NODE);

                if (null != pluginNode)
                {
                    pluginNode.Attributes[CHOME_EXTENSIBILITY_XML_ORDER_ATTRIBUTE].Value = concatedValue;
                }

                result = doc.OuterXml;
            }
            catch (Exception ex)
            {
                // TODO: add error handling.
            }

            return result;
        }
    }
}